local skynet        = require "skynet"
local proxypack     = require "proxypack"
local cjson         = require "cjson"
local pb            = require 'protobuf'
local msgdef        = require "msgdef"
local server_def    = require "server_def"
local dbdata        = require "dbdata"
local sharedata     = require "skynet.sharedata"
local util          = require "util"
local utils         = require "utils"
local player_class  = require "player_class"
local constant      = require "constant"
local data_access   = require "data_access"
local reason        = require "reason"
local error_code    = require "error_code"
local game_def      = require "game_def"
local server_config = dofile(skynet.getenv("server_config"))
local mail_def   = require "mail_def"
local table_insert = table.insert
local table_remove = table.remove
local math_floor = math.floor
local tonumber = tonumber

local handler = require "handler"
local tree_handler = require "tree_handler"
require "hall_handler"
require "daily_handler"
require "room_handler"
require "mail_handler"
require "friend_handler"
require "club_handler"
require "activity_handler"
require "award_handler"
require "welfare_handler"
require "shouchong_handler"
local shisun_handler = require "shisun_handler"

local internal = require "internal"
local notify_money_changed = handler.notify_money_changed
local finish_tree_task = tree_handler.finish_tree_task
local msg_handler

local CMD = {}
local client_fd
local player
local curr_uid
local account_id
local global_configs

local quitting  --正在退出的时候不能接受任何请求

local logining
local ip

local last_save_play_time

local last_ping_time
local DEBUG = server_config.debug
local server_name = skynet.getenv "server_name"
local agent_cache_time = tonumber(skynet.getenv "agent_cache_time") or 720
local is_review = tonumber(skynet.getenv "review") or 0

--------------------关注该session的连接情况----------------
local function watch_session(game_session,uid,observing)
    local gateway_id = game_session >> 31
    local request
    if observing then
        request = 'observe_fd'
    else
        request = 'unobserve_fd'
    end

    R().gateway(gateway_id):send('.watchdog','common',request,R().get_source(),game_session,uid)
end

---------------------------------------------------------------

local function get_msgid(msgname)
    return msgdef.name_to_id[msgname]
end

local function get_msg_module_name(msgid)
    print("msgid",msgid)
    local m = msgdef.id_to_name[msgid]
    if not m then return end
    return m[1],m[2] --[1]->module,[2]->name
end

local function send_to_gateway(msgname,msg)
    if client_fd <= 0 then
        dbglog(curr_uid,'this client is not connected',client_fd,'skip',msgname)
        return false
    end

    local msgid = get_msgid(msgname)
    if not msgid then
        skynet.error('unknown msgname',msgname,msgid)
        return
    end

    if string.find(msgname,"RSP_" ) and (not msg.description or msg.description == "") then
        if msg.result and global_configs and global_configs.error_des and global_configs.error_des[msg.result] then
            msg.description = global_configs.error_des[msg.result]
        else
            msg.description = ""
        end
    end
    if msgname ~= "hall.PONG" then 
        print("___msgname,msg___", msgname,msg)
    end
    local msg_body = pb.encode(msgname,msg)
    local package = proxypack.pack_client_message(0,msgid,msg_body)

    local gateway_id = client_fd >> 31
    R().gateway(gateway_id):send('.watchdog',server_name,'sendto',client_fd,package)

    print(string.format('[%s]<<<<<player[%s] got a request[%s] content(%s)',
        skynet.address(skynet.self()),tostring(curr_uid), msgname, msg))
    
    return true
end

local function is_client_vaild()
    return client_fd ~= -1
end

local function save_data()
    if not player then
        return
    end

    local user_data = player.user_data
    if not user_data:get_dirty_fields() then
        dbglog('there is nothing changed after last saving',curr_uid)
        return
    end

    --暂时全量存库
    local data_copy = user_data:deep_copy()
    --save player data
    R().dbsvr{key=curr_uid}:send('.msg_handler','update',curr_uid,cjson.encode(data_copy))

    user_data:clear_dirty_fields()

    if is_client_vaild() then
        local curr_time = util.get_now_time()
        --如果最后一次登录的值为0
        if player.user_data.last_login_time > 0 then
            last_save_play_time = last_save_play_time or player.user_data.last_login_time
            R().basesvr(1):send('.msg_handler','add_play_times',curr_uid,curr_time - last_save_play_time)
            last_save_play_time = curr_time
            --print("last_save_play_time",last_save_play_time,player.user_data.last_login_time)
        end
    end

end

--活动记录在线时间
local function add_act_online_time()
    if client_fd < 0 then
        return
    end

    local curr_time = util.get_now_time()
    local today_online_second = curr_time - player.user_data.last_login_time
    local same_day = util.is_same_day(player.user_data.last_login_time,curr_time)
    if not same_day then --如果跨天,今天在线时间等于 现在时间-0点时间
        local zero_time = utils.getDayZeroTime(curr_time,false)
        today_online_second = curr_time - zero_time
    end

    -- local activities = global_configs.activity
    -- for act_id,activity in pairs(activities) do
    --     if activity.ac_type == game_def.ACTIVITY_TYPE_TASK then --有任务型活动
    --         R().activitysvr(1):send('.activity_mgr','act_add_day_online_time',curr_uid,today_online_second)
    --     end
    -- end
end

local function quit()
    dbglog('player is quit now!!!')

    --记录玩家下线
    if player then
        player.user_data.line_status = false
    end

    quitting = true

    --再一次检查数据是否保存
    save_data()
    -- --还在视讯中会进行一次退出
    -- local ok, res = xpcall(shisun_handler.check_shisun_state,debug.traceback)
    -- if not ok then 
    --     errlog(res)
    -- end
    if player then
        player:billlog('logout',{r = reason.ACCOUNT_LOGOUT, continue_time = continue_time})
        add_act_online_time() --增加多少在线时长
    end
    
    R().friendsvr(1):send('.info_mgr','update_info',curr_uid,{online = false})
    skynet.send(msg_handler,'lua','on_agent_exit',curr_uid,skynet.self())
    
    skynet.exit()
end

-- local function auto_join_club(player)
--     --判断该玩家是否已经自动加入过代理商的俱乐部
--     local uid = player.uid
--     local ok,agent_user_id = R().exdbsvr(1):call('.agent','get_agent_user_id',uid)
--     if not ok then
--         errlog("user auto_join_club but call get_agent_user_id is fail",uid)
--     else
--         if agent_user_id ~= 0 and uid ~= agent_user_id then
--             local ok, ret = R().clubsvr({key=uid}):call('.msg_handler', 'add_agent_member', uid, agent_user_id)
--             if not ok then
--                 errlog("user auto_join_club but call add_proxy_member is fail",uid,agent_user_id)
--             end
--         end
--     end
-- end

local function get_vip_icon_border(vip_level)
    for k,v in pairs(global_configs.vip) do
        if v.level == vip_level then
            return v.icon_border_url
        end
    end
end

local function get_vip_salary_water_param(vip_level)
    for k,v in pairs(global_configs.vip) do
        if v.level == vip_level then
            return v.salary_water_param
        end
    end
end

local function fix_vip_level(uid,vip_level)


    if player then
        player:check_vip_open_action(vip_level)
    end

    --更换头像框
    local vip_icon_border = get_vip_icon_border(vip_level)
    if vip_icon_border then
        player.user_data.icon_border = vip_icon_border
    end
    return vip_level
end

--暂时只是游戏需要引导
local function get_player_guide_list(channel,os_system)

    local game_list = global_configs.game_list[channel] or global_configs.game_list['guanwang']
    game_list = game_list[os_system] or game_list['all'] 
    if not game_list then
        game_list = global_configs.game_list['guanwang'] or {}
        game_list = game_list[os_system] or game_list['all'] or {}
    end
    local ret = {}
    for _,game_cfg in pairs(game_list) do
        local guide_status = game_cfg.guide_status
        local guide = false
        if guide_status == 1 then   --后台配置 1：需要引导 2：不需要引导
            local game_type = game_cfg.game_type
            local aready_guide = false
            table_insert(ret,game_type)
        end
    end
    print("ffffffffffffplayer_guide_list",tostring_r(ret))
    return ret
end

local function send_system_notice_on_login() --发送系统公告跟登录的跑马灯
    R().mailsvr(1):send('.opration_message_worker','notify_system_message')
    --internal.send_opration_message(global_configs.system_notice,true)

    -- local ok,cur_index,code = R().mailsvr(1):call('.award_mgr','get_cur_index')
    -- print("send_system_notice_on_login==00",ok,cur_index,code)
    -- if not ok or not cur_index then
    --     errlog("get_cur_index==========error")
    --     return
    -- end

    -- local ok,pre_player_award_list = R().mailsvr(1):call('.award_mgr','get_pre_player_award_list',cur_index)
    -- print("send_system_notice_on_login==11",ok,tostring_r(pre_player_award_list))
    -- if not ok or not pre_player_award_list then
    --     return
    -- end

    -- if #pre_player_award_list < game_def.MESSAGE_NUM then
    --     print("send_system_notice_on_login====",#pre_player_award_list)
    --     return
    -- end

    -- local argvs = {}
    -- argvs.issue_num = cur_index - 1
    -- for k,v in pairs(pre_player_award_list) do
    --     if k <= game_def.MESSAGE_NUM then
    --         argvs["level"..k] = v.vip_level
    --         argvs["name"..k]  = v.name
    --         argvs["coin"..k]  = math_floor(v.award_coins/100) 
    --     end
    -- end
    -- argvs.player_num = #pre_player_award_list
    -- argvs.exit_time = 7*24*60*60


    -- local horse_id
    -- for k,v in pairs(global_configs.horse_message) do
    --     if (v.type or 0) == game_def.MESSAGE_TYPE_AWARD then
    --         horse_id = k
    --         break
    --     end
    -- end
    -- print("send_system_notice_on_login==22",horse_id,tostring_r(argvs))
    -- if horse_id then
    --     R().mailsvr(1):send('.opration_message_worker','send_real_horse_message',horse_id,argvs)
    -- end
end


--从php取注册信息
local function get_user_register_info(uid)
    local data = cjson.encode({uid = uid})
    local status, body = handler.common_post(constant.BECOME_AGENT, data)
    -- local ok,ret = R().accountsvr({rand=true}):call('.info_mgr','get_user_register_info',uid)
    -- if ok and ret and ret.code ~= 0 then
    --     return ret
    -- end
    --local status, body = skynet.call('.web_proxy','lua','web_post',constant.IS_TOURIST_URL,data)
    -- local status, body = httpc.request('POST',ACCOUNT_WEBSERVER_URL, 
    --     "/starry/user/is_tourist",nil,nil,body)
    print("=========get_user_register_info==========",status)
    if status ~= 200 then
        return nil
    end

    local result = cjson.decode(body)
    print("=========get_user_register_info==========",tostring_r(result))
    if result.code < 0 then
        return nil
    end
    return result
end

local function on_login(player,base_data,msg,curr_time)

    player:check_time_cross()
    local same_day = util.is_same_day(player.user_data.last_login_time,curr_time)
    player.user_data.last_login_ip = ip
    player.user_data.last_login_time = curr_time
    player.user_data.line_status = true
    local user_data = player.user_data

    if not user_data.phone_code or user_data.phone_code == "" then
        local ret = get_user_register_info(curr_uid)
        print("______get_user_register_info_______", ret)
        if ret then
            if ret.type == 'PHONE' then
                local phone = ret.union_id
                if phone then
                    player.user_data.phone_code = phone
                    print("old_player add phone ----PHONE:",phone)
                end
            end
            if ret.channel ~= "" then
                player.user_data.channel = ret.channel
                player.channel = player.user_data.channel
                local ip = ret.register_ip
                local dev = ret.dev_code
                if ip and dev then 
                    user_data.register_ip = ip
                    user_data.register_machine = dev      
                end                
            end
        end
    end

    -- if not first or not base_data then
    --     local base_data = data_access.pull_base_data(curr_uid) or {}
    -- end
    -- local base_data = player.base_data
    player.coins = base_data.coins or 0 --fix 大厅不缓存住玩家整个base数据，减少内存开销 缓存住玩家的金币 供对金币不需要准确性要求特别高的逻辑使用
    player.os_system = msg.os_system -- 客户端当前系统
    player.platform = msg.platform
    player.today_pay_money = base_data.today_pay_money --今日充值额度
        
    --暂时屏蔽周福利
--    local week_award_info = data_access.pull_weekreward_data(curr_uid) or {}
    local show_seven_announcement = false
    if not same_day or not user_data.show_seven_announcement then
        show_seven_announcement = true
    end
    user_data.show_seven_announcement = 1
    local vip_level = fix_vip_level(curr_uid,base_data.vip_level)
    
    --首充是否开启
    local shouchong_open = 1
    local sc_conf = global_configs.shouchong
    if base_data.first_charge_status == 2 or sc_conf.open_state == false then
        shouchong_open = 0
    end

    local rsp = {
        name  = user_data.name,
        gems  = base_data.gems or 0,
        coins = base_data.coins or 0,
        safe_box = base_data.safe_box or 0,
        level = user_data.level,
        icon  = user_data.icon,
        sex  = user_data.sex,
        play_total_count = user_data.play_total_count,
        play_win_count = user_data.play_win_count,
        server_time = util.get_now_time(),
        ping_interval = server_config.client_ping_interval or 0,
        roomcards = base_data.roomcards,
        is_review = is_review,
        phone_code = user_data.phone_code,
        show_seven_announcement = show_seven_announcement,
   --     yesterday_income = week_award_info.last_award,
        register_channel = user_data.channel,
        vip_level = vip_level,
        icon_border = user_data.icon_border,
        is_today_first_login = is_today_first_login,
        has_changed_name = user_data.has_changed_name,
        shouchong_open = shouchong_open,
    }

    local can_take_newbie_award = player:can_take_newbie_award()
    rsp.can_take_newbie_award = can_take_newbie_award
    local signed = false
    rsp.is_today_first_login = true
    if same_day then
        rsp.is_today_first_login = false
    end

    if util.is_same_day(curr_time,user_data.sign_info.last_time) then
        signed = true
    end
    rsp.show_signing_panel = (not signed)

    local guide_flag = player.user_data.guide_flag
    if can_take_newbie_award then
        rsp.guide_list = get_player_guide_list(player.user_data.channel,msg.os_system)
        player.user_data.guide_flag = constant.GUIDE_FLAG_YES
    end
    if not same_day then
        user_data.add_vip_count = 0
        user_data.add_login_day = 0
    end
    local vip_level_conf = utils.getDataBykv(global_configs.vip,"level",vip_level or 0)
    -- print("user_data.add_vip_count===============",user_data.add_vip_count,vip_level_conf,vip_level_conf[game_def.VIP_PRIVILEGE_TYPE_FIVE])
    -- print_r(vip_level_conf)
    -- if user_data.add_vip_count <= 0 and vip_level_conf and vip_level_conf.privilege[game_def.VIP_PRIVILEGE_TYPE_FIVE] then
    --     local ok,cur_index = R().mailsvr(1):call('.award_mgr','get_cur_index')
    --     print("cur_index=============1223",cur_index)
    --     if ok and cur_index then
    --         local count = vip_level_conf.free_num or 0
    --         print("cur_index=============555555",count)
    --         R().exdbsvr(1):send('.award_user_handler','add_free_count',curr_uid,cur_index,{vip_add_count = count,vip_level = vip_level or 0})
    --         user_data.add_vip_count = count
    --         rsp.vip_free_count = count
    --     end
    -- end

    -- if user_data.add_login_day <= 0 then
    --     R().activitysvr(1):send('.activity_mgr','act_add_login_day',curr_uid,1)
    --     user_data.add_login_day = 1
    -- end

    -- local award = global_configs.award
    -- rsp.award_open = (award and award.open) or false
    local newbie_award = global_configs.newbie_award
    rsp.bind_gold = newbie_award.bind_phone_award[1].count
  --  dbglog(tostring_r(newbie_award))
  --  dbglog("RSP_LOGIN bind_gold",rsp.bind_gold)
    send_to_gateway('login.RSP_LOGIN',rsp)
    local last_ip = user_data.last_login_ip
    print("on_login2222222222222222222222222",curr_uid, last_ip)
    player:billlog("login", {
        r = reason.ACCOUNT_LOGIN, ip = last_ip, level = user_data.level, sex = user_data.sex, 
        phone = user_data.phone_code,nick_name = user_data.name,vip_level = vip_level, register_ip = user_data.register_ip,
    })

    send_system_notice_on_login() --发送系统公告跟登录跑马灯

    --local messages = skynet.call('.opration_message_mgr','lua','get_messages')
    --internal.send_opration_message(messages)
    -- print("on_login333333333333333333333333",curr_uid, last_ip)
   
    R().friendsvr(1):send('.info_mgr','update_info',curr_uid,{
        name = user_data.name,
        icon = user_data.icon,
        sex  = user_data.sex,
        curr_game_type = 0,
        online = true,
        last_online_time = util.get_now_time(),
        icon_border = user_data.icon_border,
        cur_system = player.os_system,
        cur_platform = player.platform,
    })

    --是否在视讯里
    local shisun_table_type = user_data.shisun_table_type
    print("_____shisun_table_type__", curr_uid, shisun_table_type)
    if shisun_table_type and shisun_table_type > 0 then 
        R().friendsvr(1):send('.info_mgr','update_info',curr_uid,{curr_game_type = shisun_table_type, curr_table_type = shisun_table_type})
    end
    
    --发送vip上线公告
    if vip_level_conf and vip_level_conf.privilege[game_def.VIP_PRIVILEGE_TYPE_SEVEN] and vip_level and vip_level > 0  then
        R().mailsvr(1):send('.opration_message_worker','send_real_horse_message',constant.BROADCAST_IDS.BIDS_ALL_GAMES,{name = tostring(player.uid),vip_level = vip_level or 0})
    end


    xpcall(finish_tree_task,debug.traceback,player.user_data.tree_info,constant.TREE_TASK_TYPE.LOGIN,1)

    save_data() --登录就存下档
    -- --检查玩家的视讯游戏状态
    -- local ok, res = xpcall(shisun_handler.check_shisun_state,debug.traceback)
    -- if not ok then 
    --     errlog(res)
    -- end
    R().basesvr(1):send('.msg_handler','player_cross_time',player.uid)
    return true
end

local function do_cocall()
    --必须去拉取充值、离线的数据
    local cocall = require("cocall")
    local ok,ret = cocall(5,{
        {f = data_access.pull_user_data, id = 'user_data', params = {curr_uid}},
        --{f = data_access.pull_user_info, id = 'user_info', params = {curr_uid}},
        {f = data_access.pull_offline_data, id = 'offline_data', params = {curr_uid}},
        {f = data_access.pull_base_data, id = 'base_data', params = {curr_uid}}
    })
    if not ok then
        errlog('failed to cocall',tostring_r(ret))
    end

    local user_info
    if ret.user_data then
        if not ret.user_data.name then
            print('this is the new player',curr_uid)
            user_info = data_access.pull_user_info(curr_uid)
        end
    end

    return {
        user_data = ret.user_data,
        user_info = user_info,
        offline_data = ret.offline_data,
        base_data = ret.base_data
    }
end

local function is_forbidden(uid)
    local data = cjson.encode({uid = uid})
    local status, body = handler.common_post(constant.GET_USER_FORBIDDEN, data)
    if status == 200 and body then
        local forbidden_info = cjson.decode(body)
        if not forbidden_info then
            return false
        end
        if forbidden_info.is_forbidden then
            local rsp = {result = error_code.ACCTOUNT_IS_FORBIDDEN,forbidden_end_time=forbidden_info.end_time,forbidden_reason=forbidden_info.reason}
            send_to_gateway('login.RSP_LOGIN',rsp)
            return true
        end
    end
    return false
end


function login(msg)
    local uid = curr_uid

    -- if logining then
    --     errlog(uid,'fetching data now... please wait')
    --     return
    -- end

    --标记正在登陆
    -- logining = true

    if is_forbidden(uid) then --封号
        return
    end
    
    local curr_time = util.get_now_time()
    if player then
        --多次登录则多次返回
        dbglog("multi login", uid)
        player.device_id = msg.device_id
        player.device_brand = msg.device_brand
        player.cur_channel = msg.channel:lower()
        player.client_version = msg.client_version:lower()
        player.os_system = msg.os_system -- 客户端当前系统
        player.platform = msg.platform    
        player.ip = ip     

        local base_data = data_access.pull_base_data(curr_uid) or {}
        on_login(player,base_data,msg,curr_time)
        -- logining = false

        return
    end


    local result = do_cocall()
    local user_data = result.user_data
    if not user_data then
        errlog(uid,'invalid querying user_data')
        return
    end
    if not result.base_data then 
        errlog(uid,'invalid querying base_data')
        return
    end        

    dbglog('agent received',tostring_r(user_data))

    player = player_class.new(curr_uid,dbdata.new_from('user',user_data))
    player.diff_time = 0
    player.last_save_time = curr_time
    player.last_check_control_time = curr_time
    local user_data = player.user_data
    --print("player online ---> ",uid)

    --检查字段是否存在，不存在则补齐
    local is_new_player = false
    if player:check_user_data(result,msg) then
        is_new_player = true
    end

    _ENV.player = player

    -- player.base_data = result.base_data
    player.account_id = account_id
    player.device_id = msg.device_id
    player.device_brand = msg.device_brand
    player.channel = player.user_data.channel
    player.cur_channel = msg.channel:lower()
    player.client_version = msg.client_version:lower()
    player.ip = ip
    player.os_system = msg.os_system -- 客户端当前系统
    player.platform = msg.platform
    
    watch_session(client_fd,curr_uid,true)
    if is_new_player then
        local register_ip = util.inet_ntoa(ip)
        local params = {
            op = 'register',
            device_id = msg.device_id,
            device_brand = msg.device_brand,
            channel = user_data.channel,
            cur_channel = msg.channel:lower(),
            client_version = msg.client_version:lower(),
            register_ip = register_ip,
            name = user_data.name,
            uid = uid,
            ip = ip,
            os_system = user_data.os_system,
            platform = user_data.platform,
        }

        user_data.register_ip = register_ip
        local ret = get_user_register_info(uid)
        print("______get_user_register_info_______", ret)
        if ret then
            -- local ip = ret.register_ip
            local dev = ret.dev_code
            local phone 
            if ret.type == 'PHONE' then
                phone = ret.union_id
                if phone then
                    player.user_data.phone_code = phone
                    print("old_player add phone ----PHONE:",phone)
                end
            end
            if ret.channel ~= "" then
                player.user_data.channel = ret.channel
                player.channel = player.user_data.channel
            end
            -- user_data.register_ip = ip
            user_data.register_machine = dev or ''
            params.phone=phone
            params.register_ip=register_ip
            params.register_machine=dev      
        end
        billlog(params)

        -- R().hallsvr({key=uid}):send('.msg_handler', 'is_new_player',uid)
        -- local init_coins = player.base_data.coins
        -- billlog({op="addcoins",uid = curr_uid,curr = init_coins,value = init_coins,r = reason.GAMECOIN_REGISTER})
    else
        if user_data.phone_code == "" then
            local ret = get_user_register_info(uid)
            print("______get_user_register_info_______", ret)
            if ret then
                if ret.type == 'PHONE' then
                    local phone = ret.union_id
                    if phone then
                        player.user_data.phone_code = phone
                        print("old_player add phone ----PHONE:",phone)
                    end
                end
                if ret.channel ~= "" then
                    player.user_data.channel = ret.channel
                    player.channel = player.user_data.channel
                    local ip = ret.register_ip
                    local dev = ret.dev_code
                    if ip and dev then 
                        user_data.register_ip = ip
                        user_data.register_machine = dev      
                    end
                end
            end
        end        
    end

    on_login(player,result.base_data,msg,curr_time)
    --登陆完成
    -- logining = false
end

local function dispatch(_, _,  ...)
    if quitting then
        skynet.error('now is quitting...')
        return
    end
    local head_uid,msgid,msg,sz = ...

    local module,name = get_msg_module_name(msgid)
    if not module or not name then
        errlog('invalid msgid',msgid,module,name)
        return
    end

    local pbname = module .. '.' .. name
    local req_msg = pb.decode(pbname,msg,sz)
    if not req_msg then
        errlog('------------unknown request-----------',pbname)
        return
    end

    print(string.format('[%s]>>>>>player[%d] got a request[%s][msgid:%d][sz:%d] content(%s)',
        skynet.address(skynet.self()),curr_uid,pbname,msgid,sz,tostring_r(req_msg)))

    last_ping_time = util.get_now_time()

    if module == 'login' and name == 'REQ_LOGIN' then
        login(req_msg)

        -- dbglog("_____111111_________________login_______")
        -- local name = "hall"
        -- local module_handlers = handler.get_module_handlers(name)
        -- local hall_handler = module_handlers["REQ_TEST_SHISUN_URL"]
        -- local hall_handler = module_handlers["REQ_ENTER_SHISUN"]
        -- local hall_handler = module_handlers["REQ_LEAVE_SHISUN"]
        -- local hall_handler = module_handlers["REQ_GET_SHISUN_COIN"]
        -- local hall_handler = module_handlers["REQ_TEST_SHISUN_URL"]
        -- print("_____hall_handler___", hall_handler)
        -- skynet.fork(function()
        --     skynet.sleep(300)
        --     hall_handler({test="1234"})
        --     skynet.sleep(600)
        --     module_handlers["REQ_LEAVE_SHISUN"]()

        -- end)        
        
    else
        if not player then
            errlog('other requests required logined')
            return
        end

        local module_handlers = handler.get_module_handlers(module)
        if module_handlers and module_handlers[name] then
            local ret = module_handlers[name](req_msg,client_fd)
            if not ret then
                errlog('failed to handle requrest',pbname)
            end
        else
            errlog('unknown request',pbname)
        end
    end
end

skynet.register_protocol {
    name = "client",
    id = skynet.PTYPE_CLIENT,
    unpack = function (msg, sz)
        return proxypack.unpack_client_message(msg,sz)
    end,
    dispatch = dispatch
}

local function routine_handle()
    local curr_time = util.get_now_time()
    if curr_time - last_ping_time >= agent_cache_time then
        dbglog(curr_uid,'this guy is too long to ping, offline now')
        return quit()
    end

    if not player then
        return
    end

    if curr_time - player.last_save_time >= 300 then
        player.last_save_time = curr_time
        dbglog(curr_uid,'save to db...')
        save_data()
    end

    player:check_time_cross()
    player:check_player_control()
end

local function routine_check()
    while true do
        skynet.sleep(10 * 100)
        local ok,msg = pcall(routine_handle)
        if not ok then errlog(msg) end
    end
end

---------------------------CMD--------------------------------
function CMD.start(conf)
    msg_handler = conf.msg_handler
    client_fd = conf.fd
    curr_uid = conf.uid
    last_ping_time = util.get_now_time()
    ip = conf.ip
    account_id = conf.account_id

    skynet.fork(routine_check)
    return true
end

function CMD.instead(new_client_fd,new_ip)
    assert(client_fd ~= new_client_fd,'invalid insteading ' .. tostring(new_client_fd))
    
    last_ping_time = util.get_now_time()
    print("_________ip___new_ip___", ip, new_ip)
    if client_fd > 0 then
        send_to_gateway('login.NTF_LOGOUT',{reason = 1,description = "异地登录"})
        local gateway_id = client_fd >> 31
        R().gateway(gateway_id):send('.watchdog','hallsvr','active_close',
            client_fd,curr_uid,R().get_source())
    else
        dbglog(curr_uid,'this agent is not online')
    end

    client_fd = new_client_fd
    ip = new_ip
    watch_session(client_fd,curr_uid,true)

    return true
end

--disconnect不会清理agent,只会把client_fd设成-1
--agent等过期删除
function CMD.disconnect(reason)
    dbglog('player is offlined now',reason)

    --记录玩家下线
    if player then
        player.user_data.line_status = false
    end
    
    --先保存下数据
    save_data()

    --再打log
    if player then
        local curr_time = util.get_now_time()
        local continue_time = curr_time - player.user_data.last_login_time
        player:billlog('logout',{r = reason,continue_time = continue_time})

        add_act_online_time() --增加多少在线时长
    end

    R().friendsvr(1):send('.info_mgr','update_info',curr_uid,{online = false,last_check_day_time = util.get_now_time()})
    last_save_play_time = nil
    client_fd = -1
end

function CMD.close_server()
    dbglog('close_server save player data')
    local reason = "您已掉线"
    if client_fd > 0 then
        send_to_gateway('login.NTF_LOGOUT',{reason = 1,description = reason})
        local gateway_id = client_fd >> 31
        R().gateway(gateway_id):send('.watchdog','hallsvr','active_close',
            client_fd,curr_uid,R().get_source())
    else
        dbglog(curr_uid,'this agent is not online')
    end    
    quit()
end

function CMD.kick_player(reason)
    dbglog("_____kick_player_____",client_fd, reason)
    if client_fd > 0 then
        send_to_gateway('login.NTF_LOGOUT',{reason = 1,description = reason})
        local gateway_id = client_fd >> 31
        R().gateway(gateway_id):send('.watchdog','hallsvr','active_close',
            client_fd,curr_uid,R().get_source())
    else
        dbglog(curr_uid,'this agent is not online')
    end

    quit()
end

function CMD.common_post(url,data)
    handler.common_post(url,data)
    -- local module_handlers = handler.get_module_handlers('hall')
    -- if module_handlers and module_handlers['REQ_COMMON_POST'] then
    --     module_handlers['REQ_COMMON_POST'](url,data)
    -- end
end

function CMD.lucky_tigger(data)
    player.user_data.user_guide_time = util.get_now_time()
    send_to_gateway('hall.NTF_PLAYER_LUCKY',data)
end

function CMD.update_user_guide_gm_time()
    player.user_data.user_guide_gm_time = util.get_now_time()
end

function CMD.get_user_data(uid)
    return player.user_data
end

function CMD.get_today_continue_time(uid)
    local curr_time = util.get_now_time()
    local today_online_second = curr_time - player.user_data.last_login_time
    local same_day = util.is_same_day(player.user_data.last_login_time,curr_time)
    if not same_day then --如果跨天,今天在线时间等于 现在时间 - 0点时间
        local zero_time = utils.getDayZeroTime(curr_time,false)
        today_online_second = curr_time - zero_time
    end

    return today_online_second
end

function CMD.change_icon(icon_url)
    if icon_url then
        player.user_data.icon = icon_url
        dbglog(curr_uid,"change_icon",icon_url)
        --玩家在线通知玩家头像变化
        if player.user_data.line_status then
            send_to_gateway('hall.NTF_CHANGE_ICON',{icon = icon_url})
        end
    end
end

function CMD.on_activity_start(data)
    local ntf = {act_id = data.act_id,act_type = data.act_type}
    send_to_gateway('activity.NTF_ACTIVITY_START',ntf)
end

function CMD.on_activity_end(data)
    local ntf = {act_id = data.act_id,act_type = data.act_type}
    send_to_gateway('activity.NTF_ACTIVITY_END',ntf)
end

function CMD.on_recharge(data)
    if not player then
        return
    end
    print("on_recharge===============",data)

    local today_pay_money = data.today_pay_money
    local user_data = player.user_data
    player.today_pay_money = today_pay_money --今日充值额度
    local sign_award = global_configs.sign_award
    if user_data.welfare_sign_info.sign_state == 0 and today_pay_money >= sign_award.sign_param then 
        user_data.welfare_sign_info.sign_state = 1
        send_to_gateway('hall.NTF_RED_POINTS',{
            module_list = {constant.MODULE_DEF.WELFARE}
        })
    end



end

--个人流水增加事件
function CMD.add_person_water(data)
    if not player then
        return
    end
    local uid = data.uid
    local water_value = data.water_value
    local user_data = player.user_data
    local welfare_sign_info = user_data.welfare_sign_info
    local vip_level = data.vip_level
    local sign_award_conf = global_configs.sign_award
    local salary_water_param = get_vip_salary_water_param(vip_level)
    welfare_sign_info.month_water = (welfare_sign_info.month_water or 0) + water_value

    --检测月俸禄状态
    if welfare_sign_info.month_water >= salary_water_param and 
     welfare_sign_info.month_state == 0 and 
     welfare_sign_info.sign_count >= sign_award_conf.days_param then
        welfare_sign_info.month_state = 1
        send_to_gateway('hall.NTF_RED_POINTS',{
            module_list = {constant.MODULE_DEF.WELFARE}
        })
    end

    --摇钱树
    finish_tree_task(user_data.tree_info,constant.TREE_TASK_TYPE.WATER,water_value)

end

--个人首充流水增加事件
function CMD.add_shouchong_water(data)
    local shouchong = handler.get_module_handlers("shouchong")    
    shouchong:addPersonWater(data)
end

function CMD.add_recharge(data)
    local shouchong = handler.get_module_handlers("shouchong")    
    shouchong:addRecharge(data)
end

function CMD.on_return_water_arward(data)
    if not player then
        return
    end
    local uid = data.uid
    local water = math_floor(data.water)
    local str_date = os.date("%Y/%m/%d",data.time)
    local water_award = global_configs.water_award
    if not water_award then return end
    for _,v in pairs(water_award) do
        if v.min_water <= water and (not v.max_water or water <= v.max_water) then
            local coins = math_floor(water * v.rate / 100)
            local exinfo = player:get_extra_bill_info() --{channel = player.channel,cur_channel = player.cur_channel}
            local ok,succ,ret = R().basesvr({key=uid}):call('.msg_handler','add_coins',uid,coins,reason.WATER_RETRUN,exinfo)
            if ok and succ and ret then
               notify_money_changed(uid,{coins = ret.curr})
            end

            local mail_list = {}
            local mail = {}
            mail.uid = uid
            mail.title = string.format(mail_def.BACK_WATER_TITLE,str_date)
            mail.content = string.format(mail_def.BACK_WATER_CONTENT,str_date,water/100,coins/100)
            mail.coins = 0
            mail.mail_type = 2
            mail.op_user = mail_def.OP_USER
            table_insert(mail_list,mail)

            print("send_back_water=========",tostring_r(mail_list))
            local url = constant.ADD_MAIL_URL
            R().hallsvr(1):send('.msg_handler','common_post_data',url,{award_list = mail_list})
            break
        end
    end

    local data = {}
    data.uid = uid
    data.type = constant.DODULE_ID.DAY_REBATE  
    data.key = ""..os.date("%Y%m%d%H%M%S") .."_".. data.type .. "_" .. math.random(1,10000)
    data.op = "partake_num"
    dbglog("_____opPartakeNum__", dump(data))

    billlog(data)
    return true
end    

--更新vip等级，根据玩家流水
function CMD.on_vip_update(vip_lv,person_water)
   -- dbglog("on_vip_update",vip_lv)

    if player then
        player:check_vip_open_action(vip_lv)
    end

    --更换头像框
    local vip_icon_border = get_vip_icon_border(vip_lv)
    if vip_icon_border then
        player.user_data.icon_border = vip_icon_border
    end

    local vip_conf = global_configs.vip
    local max_vip_level = #vip_conf - 1
    local next_level_conf 
    for _,data in pairs(vip_conf) do
        if data.level == vip_lv then
            next_level_conf = data
            break
        end
    end

    if not next_level_conf then
        return
    end

    if not player then
        return
    end

    local vip_exp = vip_lv == max_vip_level and next_level_conf.charge_coins or person_water
    local ntf = {
        vip_level = vip_lv,
        vip_level_max = max_vip_level,
        vip_exp = vip_exp,
        vip_exp_max = next_level_conf.charge_coins,
        icon_border = player.user_data.icon_border,
    }

    local uid = player.uid
    local user_data = player.user_data
    local vip_award_info = user_data.vip_award_info

    for _,v in ipairs(vip_conf) do
        if v.level ~= 0 and vip_lv >= v.level then 
            local key = true
            for idx,value in pairs(vip_award_info) do
                if v.level == value.vip_level then
                    if value.status == 0 then
                        value.status = 1 
                    end
                    key = false
                    break
                end
            end
            if key then
                local found = vip_award_info:new_table_field(#vip_award_info + 1)
                found.vip_level = v.level
                found.status = 1
            end
        end
    end
    send_to_gateway('hall.NTF_RED_POINTS',{
        module_list = {constant.MODULE_DEF.WELFARE}
    })
    R().friendsvr(1):send('.info_mgr','update_info',curr_uid,{
        online = true,
        icon_border = player.user_data.icon_border,
    })
    send_to_gateway('hall.NTF_VIP_INFO',ntf) 


end

--修改用户属性
function CMD.change_property(data)
    if not data or not next(data) then
        errlog("___change_property___invoid__data_", data)
        return 
    end
    for k, v in pairs(data) do 
        player.user_data[k] = v
    end
    dbglog(curr_uid,"______change_property", data)
    --玩家在线通知玩家变化
    if player.user_data.line_status then
        send_to_gateway('hall.NTF_USER_PRO',{info = data})
    end

    return true
end

--转发消息给客户端
function CMD.send_msg(msgname, msg)
    send_to_gateway(msgname, msg)
end

-- --为活动的广播
-- function CMD.dispatch_activity_msg(msgname, msg)
    
--     send_to_gateway(msgname, msg)
-- end


---------------------------CMD--------------------------------
skynet.start(function()
    skynet.dispatch("lua", function(_,_, param,...)
        if type(param) == 'string' then
            local f = assert(CMD[param],'unknown param '.. param)
            skynet.retpack(f(...))
            return
        end

        local uid = param

        if not player then
            print('no player',uid)
            return
        end

        if quitting then
            print('already quitting',uid)
            return
        end

        if player.uid ~= uid then
            print('not the same uid',player.uid,uid)
            return
        end

        --last_ping_time = util.get_now_time()
        
        local action = (...)
        local f = internal[action]
        if not f then
            errlog('unknown action',action)
        else
            f(select(2,...))
        end
    end)

    -- sharedata.query("global_configs")

    -- global_configs = setmetatable({},{
    --     __index = function(t,k) 
    --         return sharedata.query("global_configs")[k]
    --     end
    -- })
    global_configs = setmetatable({},{
        __index = function(t,k) 
            --return sharedata.query("global_configs")[k]
            return sharedata.query(k)
        end
    })
    _ENV.global_configs = global_configs
    _ENV.send_to_gateway = send_to_gateway
    _ENV.player = nil
end)
